package com.qing.simple_player.viewmodel;

import static com.qing.simple_player.player.IPlayer.playStateToString;

import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;

import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.media3.common.MediaItem;
import androidx.media3.session.MediaSession;

import com.qing.simple_player.player.BasePlayerImpl;
import com.qing.simple_player.player.IPlayer;
import com.qing.simple_player.util.DebugLog;

import java.util.Arrays;

/**
 * 单路播放器ViewModel，播放器实现为ExoPlayer
 */
public class SinglePlayerViewModel extends AbsPlayerViewModel {

    protected final Handler mMainHandler = new Handler(Looper.getMainLooper());
    private final MutableLiveData<Integer> mPlayState = new MutableLiveData<>(PLAY_STATE_IDLE);
    private final MutableLiveData<Long> mPlayProgress = new MutableLiveData<>(0L);
    private final MutableLiveData<Long> mPlayDuration = new MutableLiveData<>(0L);
    private final IPlayer mPlayerImpl;
    private final AudioManager mAudioManager;
    private int mPendingPlaybackState = PLAY_STATE_IDLE;
    private long mPendingPlayProgress = 0L;
    private long mPendingPlayDuration = 0L;
    private final Listener mPlayStatsListener = new Listener() {
        @Override
        public void onProgressChanged(long positionMs, long durationMs) {
            setPlayProgress(positionMs);
            setPlayDuration(durationMs);
        }

        @Override
        public void onPlayStateChanged(int state) {
            setPlaybackState(state);
        }
    };
    private boolean mHasAudioFocus = false;

    public SinglePlayerViewModel(Context context, String tag, Intent sessionIntent) {
        this(context, true, tag, sessionIntent);
    }

    public SinglePlayerViewModel(Context context, boolean bindSession, String tag, Intent sessionIntent) {
        CHILD_TAG += tag;
        mPlayerImpl = new BasePlayerImpl(context, bindSession, mPlayStatsListener, tag, sessionIntent);
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        DebugLog.d(CHILD_TAG, "mPlayerImpl=" + mPlayerImpl);
    }

    public static boolean isMainThread() {
        return Looper.getMainLooper().getThread() == Thread.currentThread();
    }

    @Override
    protected String initChildTag() {
        return "SinglePlayerViewModel";
    }

    public LiveData<Integer> getPlaybackState() {
        return mPlayState;
    }

    private void setPlaybackState(@PlaySate int state) {
        DebugLog.d(CHILD_TAG, "setPlaybackState: 设置播放状态 " + playStateToString(mPendingPlaybackState) + " ==> " + playStateToString(state));
        if (mPendingPlaybackState != state) {
            mPendingPlaybackState = state;
            // postValue会导致状态中间的pending状态丢失
            runOnMainThread(() -> mPlayState.setValue(state));
        }
    }

    public LiveData<Long> getPlayProgress() {
        return mPlayProgress;
    }

    private void setPlayProgress(long positionMs) {
        if (DEBUG) {
            DebugLog.d(CHILD_TAG, "setPlayProgress: positionMs=" + positionMs + ", mPendingPosition=" + mPendingPlayProgress);
        }
        if (mPendingPlayProgress != positionMs) {
            mPendingPlayProgress = positionMs;
            if (isMainThread()) {
                mPlayProgress.setValue(positionMs);
            } else {
                mPlayProgress.postValue(positionMs);
            }
        }
    }

    public LiveData<Long> getPlayDuration() {
        return mPlayDuration;
    }

    private void setPlayDuration(long durationMs) {
        if (DEBUG) {
            DebugLog.d(CHILD_TAG, "setPlayDuration: durationMs=" + durationMs + ", mPendingDuration=" + mPendingPlayDuration);
        }
        if (mPendingPlayDuration != durationMs) {
            mPendingPlayDuration = durationMs;
            if (isMainThread()) {
                mPlayDuration.setValue(durationMs);
            } else {
                mPlayDuration.postValue(durationMs);
            }
        }
    }

    public void setMediaItem(@NonNull MediaItem mediaItem) {
        DebugLog.d(CHILD_TAG, "setMediaItem: mediaId=" + mediaItem.mediaId);
        setMediaItems(mediaItem);
    }

    @Override
    public void setMediaUrl(@NonNull String url) {
        DebugLog.d(CHILD_TAG, "setMediaItem: url=" + url);
        setMediaItem(AbsPlayerViewModel.buildMediaItem(url));
    }

    @Override
    public void setMediaItems(@NonNull MediaItem... mediaItems) {
        mPlayerImpl.setMediaItems(mediaItems);
    }

    @Override
    public void setMediaUrls(@NonNull String... urls) {
        DebugLog.d(CHILD_TAG, "setMediaItems: urls=" + Arrays.toString(urls));
        setMediaItems(Arrays.stream(urls).map(AbsPlayerViewModel::buildMediaItem).toArray(MediaItem[]::new));
    }

    public void play() {
        if (!mHasAudioFocus) {
            mHasAudioFocus = mAudioManager.requestAudioFocus(focusChange -> {
                if (focusChange == AudioManager.AUDIOFOCUS_LOSS || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
                    mHasAudioFocus = false;
                    pause();
                }
                DebugLog.d(CHILD_TAG, "handlePlay: onAudioFocusChange:" + focusChange + ", mHasAudioFocus=" + mHasAudioFocus);
            }, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
        }
        mPlayerImpl.play();
    }

    public void pause() {
        mPlayerImpl.pause();
    }

    public void stop() {
        mPlayerImpl.stop();
    }

    public void seekTo(long positionMs) {
        mPlayerImpl.seekTo(positionMs);
    }

    @CallSuper
    public void setPlaybackSpeed(float speed) {
        mPlayerImpl.setPlaybackSpeed(speed);
    }

    @Override
    public void setPlayWhenReady(boolean playWhenReady) {
        mPlayerImpl.setPlayWhenReady(playWhenReady);
    }

    @Override
    public long getCurrentPosition() {
        return mPlayerImpl.getCurrentPosition();
    }

    @Override
    public long getDuration() {
        return mPlayerImpl.getDuration();
    }

    @Override
    public void release() {
        if (mHasAudioFocus) {
            mAudioManager.abandonAudioFocus(focusChange -> DebugLog.d(TAG, "handleRelease: onAudioFocusChange:" + focusChange));
        }
        mPlayerImpl.release();
    }

    @Nullable
    @Override
    public MediaSession getMediaSession() {
        return mPlayerImpl.getMediaSession();
    }

    protected final void runOnMainThread(@NonNull Runnable runnable) {
        if (isMainThread()) {
            runnable.run();
        } else {
            mMainHandler.post(runnable);
        }
    }
}
